package com.ouc.openplatform.pampas;
import com.alibaba.dubbo.config.spring.ServiceBean;
import com.ouc.openplatform.annotation.Export;
import com.ouc.openplatform.pampas.ParamUtil.MethodInfo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class AgentImpl implements Agent, InitializingBean, ApplicationContextAware {
	private ApplicationContext applicationContext;
	private Map<String, ParamUtil.MethodInfo> methodInfoMap = new HashMap<String, MethodInfo>();

	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	public void afterPropertiesSet() throws Exception {
		Map<String, ServiceBean> serviceBeans = this.applicationContext.getBeansOfType(ServiceBean.class);
		for (ServiceBean<?> serviceBean : serviceBeans.values()) {
			Object bean = serviceBean.getRef();
			Class<?> interfaceClass = serviceBean.getInterfaceClass();
			registerMethod(bean, interfaceClass);
		}
		Map<String, Object> annotationBeans = this.applicationContext.getBeansWithAnnotation(Export.class);
		for (Object bean : annotationBeans.values()) {
			Class<?> clazz = bean.getClass();
			registerMethod(bean, clazz);
		}
	}

	private void registerMethod(Object bean, Class<?> clazz) {
		for (Method method : clazz.getDeclaredMethods()) {
			ParamUtil.MethodInfo methodInfo = ParamUtil.getMethodInfo(bean, method);
			if (methodInfo != null) {
				String key = clazz.getName() + ":" + method.getName();
				this.methodInfoMap.put(key, methodInfo);
			}
		}
	}

	public LinkedHashMap<String, String> getParamsInfo(String key) {
		ParamUtil.MethodInfo methodInfo = (ParamUtil.MethodInfo) this.methodInfoMap.get(key);
		if (methodInfo == null) {
			throw new NullPointerException("method not found: " + key);
		}
		LinkedHashMap<String, String> paramsInfo = new LinkedHashMap<String, String>();
		for (String paramKey : methodInfo.getParams().keySet()) {
			paramsInfo.put(paramKey, ((ParamUtil.ParamInfo) methodInfo.getParams().get(paramKey)).getClazz().getName());
		}
		return paramsInfo;
	}

	public WrapResp call(String key, Map<String, Object> params, Map<String, Object> context, BaseUser baseUser) {
		ParamUtil.MethodInfo methodInfo = (ParamUtil.MethodInfo) this.methodInfoMap.get(key);
		if (methodInfo == null) {
			throw new NullPointerException("method not found: " + key);
		}
		WrapResp resp = new WrapResp();
		Object[] args = new Object[methodInfo.getParams().size()];
		int index = 0;
		for (String paramName : methodInfo.getParams().keySet()) {
			ParamUtil.ParamInfo paramInfo = (ParamUtil.ParamInfo) methodInfo.getParams().get(paramName);
			Object arg = params.get(paramName);
			if ((arg instanceof InnerCookie)) {
				resp.setCookie((InnerCookie) arg);
			}
			args[(index++)] = ParamUtil.convert(arg, paramInfo.getClazz(), paramInfo.getJavaType(), context);
		}
		Object result;
		try {
			UserUtil.putCurrentUser(baseUser);
			result = methodInfo.getMethod().invoke(methodInfo.getBean(), args);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("error when invoke method: " + key, e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException("error when invoke method: " + key, e);
		} finally {
			UserUtil.clearCurrentUser();
		}
		if (result == null) {
			return resp;
		}
		if ((result instanceof Action)) {
			Action action = (Action) result;
			action.setData(ParamUtil.convertResult(action.getData()));
			resp.setResult(action);
			return resp;
		}
		if ((result instanceof Response)) {
			Response<?> response = (Response<?>) result;
			Response<Object> realResponse = new Response<Object>();
			if (response.isSuccess()) {
				realResponse.setResult(ParamUtil.convertResult(response.getResult()));
			} else {
				realResponse.setError(response.getError());
			}
			resp.setResult(realResponse);
			return resp;
		}
		resp.setResult(ParamUtil.convertResult(result));
		return resp;
	}
}
